package org.pada.domain.config.provider;

import com.fasterxml.jackson.databind.JsonNode;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pada.client.util.JsonUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;

/**
 * @author shen.peng
 * @since 2024/9/12
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class RpcRoutingConfig {

    private final RequestMappingHandlerMapping handlerMapping;

    private final RpcRouter rpcRouter;

    @PostConstruct
    public void init () {
        RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths("/rpc").methods(RequestMethod.POST).build();
        try {
            Method route = RpcRouter.class.getMethod("route", RpcBody.class, HttpServletRequest.class);
            handlerMapping.registerMapping(requestMappingInfo, rpcRouter, route);
        } catch (NoSuchMethodException e) {
            log.error("can't find router method", e);
        }

        RpcRouter.METHOD_MAP.values().forEach(x -> {
            String name = x.getDeclaringClass().getInterfaces()[0].getName();
            String url = "/" + name + "/" + x.getName();
            RequestMappingInfo requestMapping =
                    RequestMappingInfo.paths(url).methods(RequestMethod.POST).build();
            Route route = new Route() {
                @Override
                public @ResponseBody Object route(@RequestBody JsonNode body) {
                    Object o = RpcRouter.SERVICE_MAP.get(name);
                    log.info("provider url {} body {} ", url, body.toString());
                    Object invoke = RpcRouter.invoke(body, o, x);
                    log.info("provider url {} response {} ", url, JsonUtil.toJSONString(invoke));
                    return invoke;
                }
            };
            handlerMapping.registerMapping(requestMapping, route, Route.class.getDeclaredMethods()[0]);
        });
    }

    @FunctionalInterface
    public interface Route {
        @ResponseBody Object route(@RequestBody JsonNode body);
    }
}